portal Michała Hanćkowiaka
Begin main content
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <winsock2.h>
#include "c4z2res.h"

/*
Flagi - wartosci zwracane przez funkcje.
tak - akceptacja, sukces
nie - odrzucenie, porazka
Mozna rownie dobrze uzywac wartosci liczbowych
0 i 1, te stale sa tylko dla wygody.
*/
const int tak = 1;
const int nie = 0;

/*
W tej zmiennej znajdzie sie numer portu do nasluchu,
ktory zostal podany jako parametr w wierszu polecen.
*/
unsigned short nrportu;

/*
W tej zmiennej zawsze znajduje siê uchwyt
okna programu (na szczescie program ma tylko
jedno glowne okno :) ).
*/
HWND okno;

/*
Komunikat, ktory powinna wyslac funkcja WSAAsyncSelect
jesli wystapi zdarzenie FD_ACCEPT
*/
#define WM_POLACZENIE (WM_USER + 100)

/*
Z tej zmiennej prosze korzystac do tworzenia glownego
gniazda nasluchujacego. Gniazda obslugujace poszczegolne
polaczenia powinny byc zmiennymi lokalnymi w odpowiednich
procedurach.
*/
SOCKET gniazdo_nasluchujace;

/************** FUNKCJE DO WYKORZYSTANIA ******************/

/*
Ta funkcja zwraca tak lub nie w zaleznosci od tego, czy uzytkownik
akceptuje polaczenie od osoby X, czy nie.
Jesli nick X jest na liscie przyjaciol, to uzytkownik nie jest pytany
(funkcja zwraca od razu "tak").
Jesli nick'a X nie ma, to uzytkownik jest pytany czy akceptuje
komunikat od X.
Implementacja pod koniec tego modulu.
*/
int czy_akceptujesz_polaczenie(const char *odkogo, struct sockaddr_in *adr);

/*
Ta funkcja dodaje komunikat do listy komunikatow.
Implementacja pod koniec modulu.
Podajemy nick nadawcy i tresc wiadomosci.
*/
void dodaj_komunikat(const char *odkogo, const char *komunikat);


/************** FUNKCJE DO ZAIMPLEMENTOWANIA ******************/

/*
Ta funkcja powinna tworzyc gniazdo, bindowac je do podanego jako argument
portu, wywolywac listen oraz ustawiac za pomoca WSAAsyncSelect monitorowanie
gniazda pod katem accept.
Jesli zwroci "nie", to aplikacja zostanie przerwana (czyli funkcja
powinna zwracac "nie" jesli wystapil blad np. w socket, bind czy listen).
UWAGA: port zostanie podany jako liczba w formacie hosta.
Jako gniazdo nalezy wykorzystac zmienna globalna gniazdo_nasluchujace.
*/
int wystartuj_gniazdo(HWND okno_programu, unsigned short port)
{
    /* 1 */
    return tak;
}

/*
Ta funkcja powinna zaakceptowac przychodzace polaczenie, o ile nick nadawcy
znajduje sie na liscie przyjaciol lub o ile uzytkownik wyrazi zgode
na odebranie wiadomosci od danego nadawcy.
Nalezy skorzystac z wyzej zaimplementowanej funkcji czy_akceptujesz_polaczenie(...).
Funkcje accept nalezy wywolac dla globalnego gniazdo_nasluchujace.
Nalezy odczytac najpierw dlugosc wiersza tekstu (32 bity w formacie sieci),
a nastepnie wiersz tekstu o tej dlugosci.
Odczytany wiersz to nick nadawcy, ktory podajemy funkcji
czy_akceptujesz_polaczenie.
Jesli akceptacja nastapi, nalezy ponownie odczytac dlugosc wiersza tekstu
(32 bity w formacie sieci), a nastepnie sam tekst o tej dlugosci - jest
to wiadomosc.
Za pomoca wywolania funkcji dodaj_komunikat(...) nalezy wstawic wiadomosc
do okienka wiadomosci.
Funkcja ta bedzie wywolana przez petle obslugi komunikatow jako reakcja na
komunikat WM_POLACZENIE, ktory to komunikat wstawi do kolejki funkcja
WSAAsyncSelect jesli nastapi zdarzenie typu accept.
Prosze pamietac o doklejeniu '\0' na koncu odczytanych lancuchow
lub o zerowaniu calego bufora (memset) przed odczytem tekstu.
*/
void obsluz_polaczenie(void)
{
    /* 2 */
}

/*
Ta funkcja powinna nawiazac polaczenie z hostem o podanym adresie (numer portu
zawarty jest juz w parametrze).
Nastepnie powinna wyslac dwa wiersze tekstu: mojnick i msg.
Kazdy wiersz powinien byc poprzedzony wyslaniem dlugosci tekstu
jako liczby 32-bitowej w formacie sieci.
Funkcja powinna zwracac tak jesli wyslanie sie powiodlo lub nie jesli wystapil
blad.
*/
int wyslij_wiadomosc(struct sockaddr_in *odbiorca, char *mojnick, const char *msg)
{
    /* 3 */
    return tak;
}


/************************* PONIZEJ PROSZE JUZ NIC NIE ZMIENIAC *************/

BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    INITCOMMONCONTROLSEX cc;
    WORD wersja;
    WSADATA d;

    nrportu = atoi(lpCmdLine);
    if (nrportu < 1025 || nrportu > 65535) {
        MessageBox(NULL,
            "Podano nieprawid³owy numer portu lub nie podano go wcale.",
            "MINIGADU",
            MB_ICONERROR
            );
        return 1;
    }

    wersja = MAKEWORD(2,0);
    WSAStartup(wersja, &d);

    memset(&wc,0,sizeof(wc));
    wc.lpfnWndProc = DefDlgProc;
    wc.cbWndExtra = DLGWINDOWEXTRA;
    wc.hInstance = hinst;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wc.lpszClassName = "minigadu";
    RegisterClass(&wc);
    memset(&cc,0,sizeof(cc));
    cc.dwSize = sizeof(cc);
    cc.dwICC = 0xffffffff;
    InitCommonControlsEx(&cc);

    DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC) DialogFunc);

    WSACleanup();
    return 0;

}

int InitializeApp(HWND hDlg,WPARAM wParam, LPARAM lParam)
{
    okno = hDlg;
    return wystartuj_gniazdo(hDlg, nrportu);
}


/*
Deklaracja procedur, ktore sa zaimplementowane po petli
komunikatow, a sa w niej wywolywane.
*/
void wyczysc_clicked(void);
void wyslij_clicked(void);
void dodajp_clicked(void);
void usunp_clicked(void);

/*
Obsluga petli komunikatow okna.
*/
BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    case WM_INITDIALOG:
        if (InitializeApp(hwndDlg,wParam,lParam) == 0) exit(1);
        return TRUE;
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
            case IDC_WYCZYSC:
                wyczysc_clicked();
                break;
            case IDC_DODAJP:
                dodajp_clicked();
                break;
            case IDC_USUNP:
                usunp_clicked();
                break;
            case IDC_WYSLIJ:
                wyslij_clicked();
                break;
        }
        break;
    case WM_POLACZENIE:
        obsluz_polaczenie();
        break;
    case WM_CLOSE:
        EndDialog(hwndDlg,0);
        return TRUE;

    }
    return FALSE;
}

int liczba_przyjaciol(void)
{
    return (int) SendMessage(
        GetDlgItem(okno,IDC_PRZYJACIELE),
        LB_GETCOUNT,
        (WPARAM) 0,
        (LPARAM) 0
        );
}

int czy_jest_nick_na_liscie(const char *nick)
{
    int i;
    char buf[256];
    char tmpnick[256];
    char tmphost[256];
    int tmpport;
    for (i = 0; i < liczba_przyjaciol(); i++) {
        SendMessage(
            GetDlgItem(okno, IDC_PRZYJACIELE),
            LB_GETTEXT,
            (WPARAM) i,
            (LPARAM) buf
            );
        sscanf(buf,"%s",tmpnick);
        sscanf(buf+strlen(tmpnick)+3,"%s",tmphost);
        sscanf(buf+strlen(tmpnick)+strlen(tmphost)+6,"%d",&tmpport);
        if (! strcmp(tmpnick,nick)) return tak;
    }
    return nie;
}

void dodaj_przyjaciela(const char *nick, struct sockaddr_in *adr)
{
    char przyjaciel[512];
    sprintf(przyjaciel,"%s : %s : %u",
        nick,
        inet_ntoa(adr->sin_addr),
        ntohs(adr->sin_port)
        );
    SendMessage(
        GetDlgItem(okno,IDC_PRZYJACIELE),
        LB_ADDSTRING,
        (WPARAM) 0,
        (LPARAM) przyjaciel
        );
}

void usun_przyjaciela(void)
{
    int ktory;
    ktory = (int) SendMessage(
        GetDlgItem(okno, IDC_PRZYJACIELE),
        LB_GETCURSEL,
        (WPARAM) 0,
        (LPARAM) 0
        );
    if (ktory < 0) {
        MessageBox(
            NULL,
            "Nale¿y najpierw wskazaæ na liœcie przyjaciela do usuniêcia.",
            "MINIGADU",
            MB_OK|MB_ICONSTOP
            );
        return;
    }
    SendMessage(
        GetDlgItem(okno, IDC_PRZYJACIELE),
        LB_DELETESTRING,
        (WPARAM) ktory,
        (LPARAM) 0
        );
}

int czy_akceptujesz_polaczenie(const char *odkogo, struct sockaddr_in *adr)
{
    int jest;
    int decyzja;
    char buf[512];
    jest = czy_jest_nick_na_liscie(odkogo);
    if (jest) return tak;
    sprintf(buf,
        "Nadawcy %s nie ma wœród przyjació³. Czy chcesz przyj¹æ wiadomoœæ?",
        odkogo
        );
    decyzja = MessageBox(
        NULL,
        buf,
        "MINIGADU",
        MB_YESNO|MB_ICONQUESTION
        );
    if (decyzja == IDNO) return nie;
    else return tak;
}

void wyczysc_clicked(void)
{
    SendMessage(
        GetDlgItem(okno, IDC_WIADOMOSCI),
        LB_RESETCONTENT,
        (WPARAM) 0,
        (LPARAM) 0
        );
}

void usunp_clicked(void)
{
    if (liczba_przyjaciol() < 1)
        MessageBox(
            NULL,
            "Twoja lista przyjació³ jest pusta.",
            "MINIGADU",
            MB_ICONERROR
        );
    else usun_przyjaciela();
}

void dodajp_clicked(void)
{
    char nick[256];
    char host[256];
    char port[256];
    char buf[512];
    int p;
    struct hostent *he;
    struct sockaddr_in adr;

    GetDlgItemText(okno, IDC_NICKP, nick, 256);
    GetDlgItemText(okno, IDC_HOSTP, host, 256);
    GetDlgItemText(okno, IDC_PORTP, port, 256);

    if (strlen(nick)<1) {
        MessageBox(NULL, "Nie podano nick'a przyjaciela.",
            "MINIGADU", MB_ICONERROR);
        return;
    }

    p = atoi(port);
    if (p < 1025 || p > 65535) {
        MessageBox(NULL, "Numer portu jest nieprawid³owy.",
            "MINIGADU", MB_ICONERROR);
        return;
    }

    he = gethostbyname(host);
    if (he == NULL) {
        sprintf(buf,
            "Nazwa/adres hosta %s jest nieprawid³owa(y).",
            host);
        MessageBox(NULL, buf, "MINIGADU", MB_ICONERROR);
        return;
    }
    adr.sin_port = htons(p);
    adr.sin_addr = *((struct in_addr*) he->h_addr);

    if (czy_jest_nick_na_liscie(nick)) {
        sprintf(buf,
            "Przyjaciel o nick'u %s ju¿ znajduje siê na Twojej liœcie.",
            nick);
        MessageBox(NULL, buf, "MINIGADU", MB_ICONERROR);
        return;
    }

    dodaj_przyjaciela(nick, &adr);
}

void wyslij_clicked(void)
{
    char mojnick[256];
    char wiadomosc[512];
    struct sockaddr_in adr;
    int wybranyp;
    char przyjaciel[512];
    char nickp[128];
    char hostp[128];
    int portp;
    int wynik;

    GetDlgItemText(okno, IDC_MOJNICK, mojnick, 256);
    mojnick[255] = '\0';

    if (strlen(mojnick)<1) {
        MessageBox(NULL, "Musisz wprowadziæ swój nick.",
            "MINIGADU", MB_ICONERROR);
        return;
    }

    GetDlgItemText(okno, IDC_WIADOMOSC, wiadomosc, 512);
    wiadomosc[511] = '\0';

    if (strlen(wiadomosc) < 1) {
        MessageBox(NULL, "Nie mo¿esz wys³aæ pustej wiadomoœci.",
            "MINIGADU", MB_ICONERROR);
        return;
    }

    if (liczba_przyjaciol() < 1) {
        MessageBox(NULL, "Musisz mieæ co najmniej jednego przyjaciela.",
            "MINIGADU", MB_ICONERROR);
        return;
    }

    wybranyp = (int) SendMessage(
        GetDlgItem(okno, IDC_PRZYJACIELE),
        LB_GETCURSEL,
        (WPARAM) 0,
        (LPARAM) 0
        );
    if (wybranyp < 0) {
        MessageBox(NULL,
            "Wybierz przyjaciela, do którego chcesz wys³aæ wiadomoœæ.",
            "MINIGADU", MB_ICONERROR);
        return;
    }

    SendMessage(
        GetDlgItem(okno, IDC_PRZYJACIELE),
        LB_GETTEXT,
        (WPARAM) wybranyp,
        (LPARAM) przyjaciel
        );
    sscanf(przyjaciel,"%s",nickp);
    sscanf(przyjaciel+strlen(nickp)+3,"%s",hostp);
    sscanf(przyjaciel+strlen(nickp)+strlen(hostp)+6,"%d",&portp);
    adr.sin_family = AF_INET;
    adr.sin_port = htons(portp);
    adr.sin_addr.s_addr = inet_addr(hostp);
    wynik = wyslij_wiadomosc(&adr, mojnick, wiadomosc);
    if (wynik == nie) {
        MessageBox(NULL,
            "Wys³anie wiadomoœci nie powiod³o siê.",
            "MINIGADU",
            MB_ICONERROR
            );
    }
    dodaj_komunikat(mojnick, wiadomosc);
}

void dodaj_komunikat(const char *odkogo, const char *komunikat)
{
    char buf[512];
    sprintf(buf,"%s mowi: %s",odkogo,komunikat);
    SendMessage(
        GetDlgItem(okno, IDC_WIADOMOSCI),
        LB_ADDSTRING,
        (WPARAM) 0,
        (LPARAM) buf
        );
}

uwaga: portal używa ciasteczek tylko do obsługi tzw. sesji...